/*******************************************************************************
* This file provides the API for the I2C module.
*
* Author: Kong Wai Weng
* Email:  waiweng83@gmail.com
*******************************************************************************/



#include "hal_i2c.h"



/*******************************************************************************
* PUBLIC FUNCTION: vInitI2c1
*
* PARAMETERS:
* ~ void
*
* RETURN:
* ~ void
*
* DESCRIPTIONS:
* Initialize the I2C 1 module.
*
*******************************************************************************/
void vInitI2c1(void)
{
	// Disbale I2C mdolue if enabled previously.
	CloseI2C1();
	
	// Make sure the SCL pin is pulled up properly.
	while (I2C1_SCL == 0) {
		// Blink the LED.
		LED1 ^= 1;
		__delay_ms(50);
	}	
	
	// If the SDA pin is being hold by the slave, clock the SCL pin until the SDA is released.
	if (I2C1_SDA == 0) {
		I2C1_SCL_TRIS = 0;
		
		while (I2C1_SDA == 0) {
			// Send the clock and blink the LED.
			I2C1_SCL ^= 1;
			LED1 ^= 1;
			__delay_ms(50);
		}
		
		I2C1_SCL_TRIS = 1;
	}	
	
	// Configure I2C interrupt.
	ConfigIntI2C1(	MI2C1_INT_OFF &		// Master interrupt disabled.
					MI2C1_INT_PRI_0 &	// Master interrupt priority 0.
					SI2C1_INT_OFF &		// Slave interrupt disabled.
					SI2C1_INT_PRI_0 );	// Slave interrupt priority 0.
	
	// Calculate the value for I2C1BRG for 400kHz.
	unsigned int uiI2cBrg1 = (unsigned int)(((float)FCY / 400000.0) - ((float)FCY / 10000000.0) - 0.5);
	
	// Open the I2C.
	OpenI2C1(	I2C1_ON	&			// Enable I2C.
				I2C1_IDLE_CON &		// Continue I2C module in Idle mode.
				I2C1_CLK_REL &		// Release clock.
				I2C1_IPMI_DIS &		// IPMI mode not enabled.
				I2C1_7BIT_ADD &		// I2CADD is 7-bit address.
				I2C1_SLW_EN &		// Enable Slew Rate Control for 400KHz.
				I2C1_SM_DIS &		// Disable SM bus specification.
				I2C1_GCALL_DIS &	// Disable general call address.
				I2C1_STR_DIS &		// Disable clock stretching.
				I2C1_NACK &			// Transmit 1 to send NACK as acknowledge.
				I2C1_ACK_DIS &		// Acknowledge sequence not in progress.
				I2C1_RCV_DIS &		// Receive sequence not in progress.
				I2C1_STOP_DIS &		// Stop condition idle.
				I2C1_RESTART_DIS &	// Restart condition idle.
				I2C1_START_DIS,		// Start condition Idle.
				uiI2cBrg1		);	// Baud rate.
}	



/*******************************************************************************
* PUBLIC FUNCTION: uiReadI2c1
*
* PARAMETERS:
* ~ uiI2cAddress	- The I2C slave address.
* ~ uiRegAddress	- The register address to read.
* ~ uiBytesCount	- Number of bytes to read.
* ~ pucBuffer		- Buffer for the received data.
*
* RETURN:
* ~ Return 1 if error occured.
* ~ Return 0 if no error.
*
* DESCRIPTIONS:
* Read from I2C 1.
*
*******************************************************************************/
unsigned int uiReadI2c1(	unsigned int uiI2cAddress,
							unsigned int uiRegAddress,
							unsigned int uiBytesCount,
							unsigned char* pucBuffer	)
{
	unsigned char ucReturnCode = 0;
	
	// Take control of I2C1.
	// Wait forever until the resource is available.
	xSemaphoreTake(mutexI2cBusy1, portMAX_DELAY);
	
	// Send start bit.
	StartI2C1();
	IdleI2C1();
	
	// Send slave address and indicate to write.
	// If write collision occured or slave did not acknowledge, return 1.
	if (MasterWriteI2C1(uiI2cAddress << 1) != 0) {
		ucReturnCode = 1;
		goto EXIT;
	}	
	
	// Write the register address to the I2C slave.
	// If write collision occured or slave did not acknowledge, return 1.
	if (MasterWriteI2C1(uiRegAddress) != 0){
		ucReturnCode = 1;
		goto EXIT;
	}	
	
	
	
	// Send repeated start.
	RestartI2C1();
	IdleI2C1();

	// Send slave address and indicate to write.
	// If write collision occured or slave did not acknowledge, return 1.
	if (MasterWriteI2C1((uiI2cAddress << 1) | 0x01) != 0) {
		ucReturnCode = 1;
		goto EXIT;
	}	
	
	
	// Read the data from the slave.
	// If error occured, return 1.
	if (MastergetsI2C1(uiBytesCount, pucBuffer, 65535) != 0) {
		ucReturnCode = 1;
		goto EXIT;
	}	

EXIT:
		
	// Send stop bit.
	StopI2C1();
	IdleI2C1();
	
	// Release control of I2C 1.
	xSemaphoreGive(mutexI2cBusy1);
	
	// Return with return code.
	return ucReturnCode;
}	



/*******************************************************************************
* PUBLIC FUNCTION: uiWriteI2c1
*
* PARAMETERS:
* ~ uiI2cAddress	- The I2C slave address.
* ~ uiRegAddress	- The register address to read.
* ~ uiBytesCount	- Number of bytes to read.
* ~ pucBuffer		- Buffer for the received data.
*
* RETURN:
* ~ Return 1 if error occured.
* ~ Return 0 if no error.
*
* DESCRIPTIONS:
* Write to I2C 1.
*
*******************************************************************************/
unsigned int uiWriteI2c1(	unsigned int uiI2cAddress,
							unsigned int uiRegAddress,
							unsigned int uiBytesCount,
							unsigned char* pucBuffer	)
{
	unsigned char ucReturnCode = 0;
	
	// Take control of I2C1.
	// Wait forever until the resource is available.
	xSemaphoreTake(mutexI2cBusy1, portMAX_DELAY);
	
	// Send start bit.
	StartI2C1();
	IdleI2C1();
	
	// Send slave address and indicate to write.
	// If write collision occured or slave did not acknowledge, return 1.
	if (MasterWriteI2C1(uiI2cAddress << 1) != 0) {
		ucReturnCode = 1;
		goto EXIT;
	}	
	
	// Write the register address to the I2C slave.
	// If write collision occured or slave did not acknowledge, return 1.
	if (MasterWriteI2C1(uiRegAddress) != 0){
		ucReturnCode = 1;
		goto EXIT;
	}	
	
	// Write data.
	// Return 1 if error occured.
	while (uiBytesCount-- > 0) {
		if (MasterWriteI2C1(*pucBuffer++) != 0){
			ucReturnCode = 1;
			goto EXIT;
		}	
	}	
	
EXIT:
		
	// Send stop bit.
	StopI2C1();
	IdleI2C1();
	
	// Release control of I2C 1.
	xSemaphoreGive(mutexI2cBusy1);
	
	// Return with return code.
	return ucReturnCode;
}	
